zlib: add ZIP archive support to zlib (ZipFile,ZipBuffer,ZipEntry)#64339
zlib: add ZIP archive support to zlib (ZipFile,ZipBuffer,ZipEntry)#64339pipobscure wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #64339 +/- ##
==========================================
+ Coverage 90.25% 90.31% +0.05%
==========================================
Files 741 742 +1
Lines 241165 243596 +2431
Branches 45423 46034 +611
==========================================
+ Hits 217673 219994 +2321
- Misses 15069 15157 +88
- Partials 8423 8445 +22
🚀 New features to boost your workflow:
|
This comment was marked as outdated.
This comment was marked as outdated.
0f2b879 to
4258e94
Compare
Codecov flagged low patch coverage on lib/internal/zip.js and lib/internal/vfs/providers/archive.js in nodejs#64339. Add tests exercising Zip64 extra-field parsing, DOS date/time edge cases, streaming-entry state guards, decodeMemberStream/decodeMemberSync's duplicated error branches, the ZipBuffer/ZipFile iteration protocols, several on-disk ZipFile error paths, and ArchiveFileHandle's direct read/write/stat/ truncate surface plus a handful of provider-level error branches the existing tests didn't reach.
2a56a15 to
27ab6b4
Compare
|
See also #45651 |
Thanks @bakkot !!! I think the time has come for it on the one hand, and on the other I added some „motivation“ links earlier. Here some more detail: Based on this, we can modify the loader to directly load from an archive. If we do that, we get application bundles. pipobscure#5 & pipobscure#6 Which can then in turn be used to easily create application bundles: https://github.com/pipobscure/experimental-sea So the world had changed enough that it‘s worth proposing again. |
f157686 to
233f865
Compare
|
I like this a lot. It's likely better to split this into 2 PRs, one for Zip support and one for VFS-Zip, so the Zip support could theoretically be backportable on its own. |
| writeFileSync('archive.zip', await zip.toBuffer()); | ||
| } | ||
| main(); | ||
| ``` |
There was a problem hiding this comment.
Does this support streamed reading? In other words, do we need to load the full buffer in memory?
There was a problem hiding this comment.
Given that ZIP files have their Central Directory at the end, streaming (as in over the network) isn't really a thing. However in terms of reading from a ZipFile, then the asnwer is yes. It loads the Directory into memory. After that it knows exactly the bytes it needs to read a file, so it does a partial file read. So no you don't need to load the entire archive into memory. (I hope I understood what you are asking)
There was a problem hiding this comment.
I validated streaming again. Yes it's does fully streaming reads and writes.
Read: create a stream directed at the file (offset based) and pipe through decompression.
Write: ZipEntry.createStream(inputstream) and then createZipArchive([ entry ]) which creates a Readable that can be piped. The input stream is consumed as the output stream is written.
So both directions are fully streamable (with the mentioned exception in reading requiring the end of the data to be available)
There was a problem hiding this comment.
If we could support that via some kind of API, it would be awesome. To repeat: instead of a Buffer, specify a filename to be read in chunks.
This can also be a separate API, in case we want it to be made async.
There was a problem hiding this comment.
Do you mean something like
createZipFromFiles([ filename1, filename2, filename3 ])that just creates a Redable that can be piped?
The difficulty is that the name inside the archive isn't the absolute filename. Where do you cut it off?
If we want a simple createZipFromFolder(absoluteFolder) that creates a Readable, then give me 5 minutes.
There was a problem hiding this comment.
Alternatively we could do:
createZipArchiveSomething([ [ fullpath, nameinarchive ] ])
This comment was marked as outdated.
This comment was marked as outdated.
|
As per @mcollina I split this into two PRs. I have the vfs-provider ready to go as follow up one (as it depends on this being merged) I also made sure that the streaming side of things was actually as clean as I intended, and added a few more tests. |
Add ZIP archive support to the node:zlib module through three classes and a set of helpers: - ZipEntry: a single archive member, with buffered reads (content()), bounded-memory streaming reads (contentIterator()), and create()/createStream() for building members. - ZipFile: random access to an archive backed by a file descriptor, reading members lazily without retaining their content and writing new members in place; opened with open()/openSync(). - ZipBuffer: a zero-copy, in-memory view over an archive already held in a Buffer. createZipArchive() serializes a sequence of entries into an archive byte stream, and setMaxZipContentSize() bounds the default in-memory decompression size. Every operation has both an asynchronous and a synchronous form. Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
Summary
Add ZIP archive support to the node:zlib module through three classes
and a set of helpers:
bounded-memory streaming reads (contentIterator()), and
create()/createStream() for building members.
reading members lazily without retaining their content and writing
new members in place; opened with open()/openSync().
in a Buffer.
createZipArchive() serializes a sequence of entries into an archive
byte stream, and setMaxZipContentSize() bounds the default in-memory
decompression size. Every operation has both an asynchronous and a
synchronous form.
P.S.: my CLA should be on file and I wrote this myself so COO is declared